/*
 * (C) Copyright 2004 Texas Instruments
 * Jian Zhang <jzhang@ti.com>
 *
 *  Samsung K9F1G08R0AQ0C NAND chip driver for an OMAP2420 board
 * 
 * This file is based on the following u-boot file:
 *	common/cmd_nand.c
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>

#include <asm/arch/sys_proto.h>
#include <asm/arch/sys_info.h>

#ifdef CFG_NAND_K9F1G08R0A

#define K9F1G08R0A_MFR		0xec  /* Samsung */
#define K9F1G08R0A_ID		0xa1  /* part # */

/* Since Micron and Samsung parts are similar in geometry and bus width
 * we can use the same driver. Need to revisit to make this file independent
 * of part/manufacturer
 */
#define MT29F1G_MFR		0x2c  /* Micron */
#define MT29F1G_MFR2		0x20  /* numonyx */
#define MT29F1G_MFR3		0xad  /* Hynix */
#define MT29F1G_ID		0xa1  /* x8, 1GiB */
#define MT29F2G_ID      	0xba  /* x16, 2GiB */
#define MT29F4G_ID		0xbc  /* x16, 4GiB */

#define ADDR_COLUMN		1          
#define ADDR_PAGE		2             
#define ADDR_COLUMN_PAGE	(ADDR_COLUMN | ADDR_PAGE)

#define ADDR_OOB		(0x4 | ADDR_COLUMN_PAGE) 

#define PAGE_SIZE		2048
#define MAX_NUM_PAGES		64

/*******************************************************
 * Routine: delay
 * Description: spinning delay to use before udelay works
 ******************************************************/
static inline void delay (unsigned long loops)
{
	__asm__ volatile ("1:\n"
					  "subs %0, %0, #1\n"
					  "bne 1b":"=r" (loops):"0" (loops));
}

static int nand_read_page(u_char *buf, ulong page_addr);

/* NanD_Command: Send a flash command to the flash chip */
static int NanD_Command(unsigned char command)
{
 	NAND_CTL_SETCLE(NAND_ADDR);

 	WRITE_NAND_COMMAND(command, NAND_ADDR);
 	NAND_CTL_CLRCLE(NAND_ADDR);

  	if(command == NAND_CMD_RESET){
		unsigned char ret_val;
		NanD_Command(NAND_CMD_STATUS);
		do{
			ret_val = READ_NAND(NAND_ADDR);/* wait till ready */
  		} while((ret_val & 0x40) != 0x40);
 	}
 	
	NAND_WAIT_READY();
	return 0;
}


/* NanD_Address: Set the current address for the flash chip */
static int NanD_Address(unsigned int numbytes, unsigned long ofs)
{
	uchar u;

 	NAND_CTL_SETALE(NAND_ADDR);

		ushort col = ofs;

		u = col  & 0xff;
		WRITE_NAND_ADDRESS(u, NAND_ADDR);

		u = (col >> 8) & 0x07;
		if (numbytes == ADDR_OOB)
			u = u | 4;
		WRITE_NAND_ADDRESS(u, NAND_ADDR);

		u = (ofs >> 11) & 0xff;
		WRITE_NAND_ADDRESS(u, NAND_ADDR);
		u = (ofs >> 19) & 0xff;
		WRITE_NAND_ADDRESS(u, NAND_ADDR);

		/* One more address cycle for devices > 128MiB */
			u = (ofs >> 27) & 0xff;
			WRITE_NAND_ADDRESS(u, NAND_ADDR);

 	NAND_CTL_CLRALE(NAND_ADDR);

 	NAND_WAIT_READY();
	return 0;
}

/* read chip mfr and id
 * return 0 if they match board config
 * return 1 if not
 */
int nand_chip()
{
	NAND_ENABLE_CE();

	NanD_Command(NAND_CMD_RESET);
 
	NAND_DISABLE_CE();
}

/* read a block data to buf
 * return 1 if the block is bad or ECC error can't be corrected for any page
 * return 0 on sucess
 */ 
int nand_read_block(unsigned char *buf, ulong block_addr)
{
	int i, offset = 0;

	/* read the block page by page*/
	for (i=0; i<MAX_NUM_PAGES; i++){
		if (nand_read_page(buf+offset, block_addr + offset))
			return 1;
		offset += PAGE_SIZE;
	}

	return 0;
}
/* read a page with ECC */
static int nand_read_page(u_char *buf, ulong page_addr)
{
	u16 val;
	int cntr;
	u16 *p;

	NAND_ENABLE_CE();   
	NanD_Command(NAND_CMD_READ0);
	NanD_Address(ADDR_COLUMN_PAGE, page_addr);
	NanD_Command(NAND_CMD_READSTART);
	NAND_WAIT_READY();

	/* A delay seems to be helping here. needs more investigation */
	delay(10000);
	p = buf;
	for (cntr = 0; cntr < 1024 ; cntr++){
		*p++ = READ_NAND(NAND_ADDR);
		delay(10);
   	}
	
	return 0;
}

#endif
